Imports System
Imports System.Collections

Public MustInherit Class CollectionTemplate
    Implements IList
    Implements ICollection
    Implements IEnumerable

    Private _list As ArrayList

    Protected Sub New()
        _list = New ArrayList()
    End Sub


    Protected ReadOnly Property InnerList() As ArrayList
        Get
            If _list Is Nothing Then _list = New ArrayList()
            Return _list
        End Get
    End Property

    Protected ReadOnly Property List() As IList
        Get
            Return CType(Me, IList)
        End Get
    End Property

    Public ReadOnly Property Count() As Int32 Implements IList.Count
        Get
            If _list Is Nothing Then
                Return 0
            Else
                Return _list.Count
            End If
        End Get
    End Property

    Private ReadOnly Property IsSynchronized() As Boolean Implements ICollection.IsSynchronized
        Get
            Return InnerList.IsSynchronized
        End Get
    End Property

    Private ReadOnly Property SyncRoot() As Object Implements ICollection.SyncRoot
        Get
            Return InnerList.SyncRoot
        End Get
    End Property

    Private ReadOnly Property IsReadOnly() As Boolean
        Get
            Return InnerList.IsReadOnly
        End Get
    End Property

    Private Sub CopyTo(ByVal arr As Array, ByVal index As Int32) Implements ICollection.CopyTo
        InnerList.CopyTo(arr, index)
    End Sub

    Default Property Item(ByVal index As Int32) As Object Implements IList.Item
        Get
            If (index < 0) Or (index >= InnerList.Count) Then
                Throw New ArgumentException("index out of bounds")
            Else
                Return InnerList(index)
            End If
        End Get
        Set(ByVal Value As Object)
            If (index < 0) Or (index >= InnerList.Count) Then
                Throw New ArgumentException("index out of bounds")
            Else
                OnValidate(Value)
                If OnSetting(index, InnerList(index), Value) Then
                    InnerList(index) = Value
                    OnSet(index, Value)
                End If
            End If
        End Set
    End Property

    Private Function Contains(ByVal value As Object) As Boolean Implements IList.Contains
        Return InnerList.Contains(value)
    End Function

    Private Function Add(ByVal value As Object) As Int32 Implements IList.Add
        OnValidate(value)
        If OnInserting(InnerList.Count, value) Then
            Dim index As Int32 = InnerList.Add(value)
            OnInserted(index, value)
        End If
    End Function

    Public Sub Clear() Implements IList.Clear
        If OnClearing() Then
            InnerList.Clear()
            OnCleared()
        End If
    End Sub

    Private Sub Remove(ByVal value As Object) Implements IList.Remove
        OnValidate(value)
        Dim index As Int32 = InnerList.IndexOf(value)
        If index < 0 Then
            Throw New ArgumentException()
        Else
            If OnRemoving(index, value) Then
                InnerList.RemoveAt(index)
                OnRemoved()
            End If
        End If
    End Sub

    'does this need to be public
    Public Sub RemoveAt(ByVal index As Int32) Implements IList.RemoveAt
        If (index < 0) Or (index >= InnerList.Count) Then
            Throw New ArgumentException()
        Else
            OnValidate(InnerList(index))
            If OnRemoving(index, InnerList(index)) Then
                InnerList.RemoveAt(index)
                OnRemoved()
            End If
        End If
    End Sub


    Private Function IndexOf(ByVal value As Object) As Int32 Implements IList.IndexOf
        Return InnerList.IndexOf(value)
    End Function


    Private Sub Insert(ByVal index As Int32, ByVal value As Object) Implements IList.Insert
        If (index < 0) Or (index >= InnerList.Count) Then
            Throw New ArgumentException()
        Else
            OnValidate(value)
            If OnInserting(index, value) Then
                InnerList.Insert(index, value)
                OnInserted(index, value)
            End If
        End If
    End Sub


    Private Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
        Return InnerList.GetEnumerator()
    End Function


    Protected Overridable Function OnSetting(ByVal index As Int32, _
       ByVal oldValue As Object, ByVal newValue As Object) As Boolean
        Return True
    End Function

    Protected Overridable Sub OnSet(ByVal index As Int32, ByVal value As Object)
        'called when an item get's set
    End Sub

    Protected Overridable Function OnInserting(ByVal index As Int32, ByVal value As Object) As Boolean
        Return True
    End Function

    Protected Overridable Sub OnInserted(ByVal index As Int32, ByVal value As Object)
        'called when an item is inserted/added
    End Sub

    Protected Overridable Function OnClearing() As Boolean
        Return True
    End Function

    Protected Overridable Sub OnCleared()
        'list has been cleared
    End Sub

    Protected Overridable Function OnRemoving(ByVal index As Int32, ByVal value As Object) As Boolean
        Return True
    End Function

    Protected Overridable Sub OnRemoved()
        'item has been removed
    End Sub

    Protected Overridable Sub OnValidate(ByVal value As Object)
        If value Is Nothing Then Throw New ArgumentException()
    End Sub

End Class